package com.honeybees.framework.common.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.time.DateUtils;
import org.joda.time.Duration;
import org.joda.time.Period;

/**
 * 日期工具类
 *
 * @author zyj8822
 */
public class DateUtil {

    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public final static DateFormat FORMAT_DATE_TIME = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /**
     * yyyy-MM-dd
     */
    public final static DateFormat FORMAT_DATE = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * yyyy-MM-dd EEE
     */
    public final static DateFormat FORMAT_DATE_WEEK = new SimpleDateFormat("yyyy-MM-dd EEE");

    /**
     * 获取当前系统纳秒时间
     *
     * @return
     */
    public static long nanoTime() {
        return System.nanoTime();
    }

    /**
     * <DL>
     * <DT><B> 获取当前时间 </B></DT>
     * <p>
     * <DD>yyyy-MM-dd HH:mm:ss</DD>
     * </DL>
     * <p>
     *
     * @return yyyy-MM-dd HH:mm:ss
     * @version 2014-10-09 15:08:11
     */
    public static String getDatetime() {
        return FORMAT_DATE_TIME.format(new Date());
    }

    /**
     * <DL>
     * <DT><B> 解析字符串为Date </B></DT>
     * <p>
     * <DD>yyyy-MM-dd HH:mm:ss</DD>
     * </DL>
     * <p>
     *
     * @param date 字符串
     * @return yyyy-MM-dd HH:mm:ss
     * @version 2014-10-09 15:12:00
     */
    public static Date parseDateTime(String date) {
        try {
            return FORMAT_DATE_TIME.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException("不能将字符串解析为日期，请检查格式是否正确：" + date);
        }
    }

    /**
     * <dl>
     * <dt><b> 解析字符串为Date </b></dt>
     * <p>
     * <dd>yyyy-MM-dd</dd>
     * </dl>
     *
     * @param date
     * @return
     * @author 李远明
     * @version 2016年7月20日 上午10:56:28
     */
    public static Date parseDate(String date) {
        try {
            return FORMAT_DATE.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException("不能将字符串解析为日期，请检查格式是否正确：" + date);
        }
    }

    /**
     * <DL>
     * <DT><B> 格式化日期，解决从MySQL中查出来的日期后面多了“.0”的问题 </B></DT>
     * <p>
     * <DD>2014-10-09 15:15:12</DD>
     * </DL>
     * <p>
     *
     * @param date Date
     * @return 2014-10-09 15:15:12
     * @version 2014-10-09 15:15:12
     */
    public static String formatDateTime(Date date) {
        return FORMAT_DATE_TIME.format(date);
    }

    /**
     * <dl>
     * <dt><b> 格式化日期 </b></dt>
     * <p>
     * <dd>yyyy-MM-dd</dd>
     * </dl>
     *
     * @param date
     * @return
     * @author 李远明
     * @version 2016年7月20日 上午11:00:43
     */
    public static String formatDate(Date date) {
        return FORMAT_DATE.format(date);
    }

    /**
     * <dl>
     * <dt><b> 格式化日期，包含星期 </b></dt>
     * <p>
     * <dd>yyyy-MM-dd EEE</dd>
     * </dl>
     *
     * @param date
     * @return 2016-06-27 星期一
     * @author 李远明
     * @version 2016年7月20日 上午11:32:16
     */
    public static String formatDateWeek(Date date) {
        return FORMAT_DATE_WEEK.format(date);
    }

    /**
     * <DL>
     * <DT><B> 得到两个日期之间的月份（yyyy-MM） </B></DT>
     * <p>
     * <DD>功能描述</DD>
     * </DL>
     * <p>
     *
     * @param start 开始日期（yyyy-MM-dd）
     * @param end   结束日期（yyyy-MM-dd）
     * @return
     * @author JacobLee
     * @version 2014-12-07 11:36:47
     */
    public static List<String> getMonthsBetween(String start, String end) {
        Date date1 = new Date(), date2 = new Date();
        try {
            date1 = FORMAT_DATE.parse(start); // 开始日期
        } catch (ParseException e) {
            e.printStackTrace();
        }
        try {
            date2 = FORMAT_DATE.parse(end); // 结束日期
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();

        List<String> monthList = new ArrayList<String>();
        c1.setTime(date1);
        c2.setTime(date2);
        while (c1.compareTo(c2) < 0) {
            c1.add(Calendar.MONTH, 1); // 开始日期加一个月直到等于结束日期为止
            Date ss = c1.getTime();
            String str = getMonth(ss);
            monthList.add(str);
        }
        int dayOfMonth = c1.get(Calendar.DAY_OF_MONTH);
        if (dayOfMonth == 1) { // 如果第一个日期是一个月的第一天，则将第一个日期也包括。
            monthList.add(0, getMonth(date1));
        }

        if (monthList.size() == 0) {
            monthList.add(getMonth(date1));
        }

        return monthList;
    }

    private static String getMonth(Date date) {
        String str = FORMAT_DATE.format(date);
        str = str.substring(0, str.lastIndexOf("-"));
        return str;
    }

    /**
     * <dl>
     * <dt><b> 计算两个日期之间相差的时间 </b></dt>
     * <p>
     * <dd><b>大于等于一天的，返回天数；不足一天的，返回小时数；不足一小时的，返回分钟数。</b></dd>
     * <p>
     * <dd>用于计算项目到期日期。</dd>
     * </dl>
     *
     * @param fromDate 开始日期
     * @param toDate   到达日期
     * @return 开始时间距离到达时间还有多久（天数/时间）
     * @author 李远明
     * @version 2016-05-04 14:08:58 新建
     * @version 2016-05-23 10:56:46 {@link org.joda.time.Duration}
     */
    public static String getIntervalTime(Date fromDate, Date toDate) {
        if (fromDate == null || toDate == null) {
            throw new IllegalArgumentException("时间不能为null");
        }
        long fromTime = fromDate.getTime();
        long toTime = toDate.getTime();

        Duration duration = new Duration(fromTime, toTime);

        long hours = duration.getStandardHours();

        String time = null; // 相差的时间
        if (Math.abs(hours) < 24) { // 相差的时间在正负24小时之内（一天24个小时）
            time = hours + "小时";
            if (Math.abs(hours) < 1) { // 相差的时间在正负1小时之内
                long minutes = duration.getStandardMinutes();
                time = minutes + "分钟";
            }
        } else { // 相差时间超过正负一天
            long days = duration.getStandardDays();
            time = days + "天";
        }
        return time;
    }

    /**
     * <dl>
     * <dt><b> 计算前一个时间比后一个时间过去多久时间 </b></dt>
     * <p>
     * <dd>可以计算月份</dd>
     * </dl>
     *
     * @param fromDate 时间1
     * @param toDate   时间2
     * @return 时间1与时间2相比，多去了多久（天数/时间）
     * @author 李远明
     * @version 2016-06-07 19:06:14 {@link org.joda.time.Period}
     */
    public static String getPeriodTime(Date fromDate, Date toDate) {
        if (fromDate == null || toDate == null) {
            throw new IllegalArgumentException("时间不能为null");
        }
        long fromTime = fromDate.getTime();
        long toTime = toDate.getTime();

        String result = "时间未到达";
        if (fromTime > toTime) {
            return result;
        }

        Period period = new Period(fromTime, toTime);

        int months = period.getMonths();
        if (months >= 1) {
            result = months + "月前";
        } else {
            int weeks = period.getWeeks();
            if (weeks >= 1) {
                result = weeks + "星期前";
            } else {
                int days = period.getDays();
                if (days >= 1) {
                    result = days + "天前";
                } else {
                    int hours = period.getHours();
                    if (hours >= 1) {
                        result = hours + "小时前";
                    } else {
                        int minutes = period.getMinutes();
                        if (minutes >= 1) {
                            result = minutes + "分钟前";
                        } else {
                            result = "刚刚";
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * <dl>
     * <dt><b> 判断是否为闰年 </b></dt>
     * <p>
     * <dd>置闰法则：四年一闰；百年不闰，四百年再闰</dd>
     * <p>
     * <dd>精确计算方法：（按一回归年365天5小时48分45.5秒）</dd>
     * <dd>①、普通年能被4整除且不能被100整除的为闰年。（如2004年就是闰年，1900年不是闰年）</dd>
     * <dd>②、世纪年能被400整除的是闰年。（如2000年是闰年，1900年不是闰年）</dd>
     * <dd>③、对于数值很大的年份，这年如果能整除3200，并且能整除172800则是闰年。如172800年是闰年，86400年不是闰年。<br>
     * （因为虽然能整除3200，但不能整除172800）（此按一回归年365天5小时48分45.5秒计算）</dd>
     * </dl>
     *
     * @param year 年份
     * @return true/false
     * @author 李远明
     * @version 2016-08-11 09:56:30
     * @see org.quartz.CronExpression#isLeapYear(int)
     * @see sun.util.calendar.CalendarUtils#isGregorianLeapYear(int)
     */
    @SuppressWarnings("restriction")
    public static boolean isLeapYear(int year) {
        return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
    }

    /**
     * <dl>
     * <dt><b> 获取指定年份，指定月份的天数 </b></dt>
     * <p>
     * <dd>功能描述</dd>
     * </dl>
     *
     * @param monthNum 月份：0～11，值与 {@link java.util.Calendar#MONTH} 相同
     * @param year     年份
     * @return 月份的天数
     * @author 李远明
     * @version 2016-08-11 11:33:46
     * @see org.quartz.CronExpression#getLastDayOfMonth(int, int)
     * @see java.util.GregorianCalendar#monthLength(int, int)
     */
    public static int getLastDayOfMonth(final int monthNum, final int year) {
        if (monthNum < 0 || monthNum > 11) {
            throw new IllegalArgumentException("Illegal month number: " + monthNum + ", valid is 0 to 11, inclusive");
        }
        int MONTH_LENGTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 平年
        int LEAP_MONTH_LENGTH[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 闰年

        int days = isLeapYear(year) ? LEAP_MONTH_LENGTH[monthNum] : MONTH_LENGTH[monthNum];
        return days;
    }

    public static void main(String[] args) {
        int days = getLastDayOfMonth(1, 2019); // 二月
        System.out.println(days);

        Date datetime = parseDateTime("2016-07-20 11:23:12.0");
        datetime = new Date();

        // 一个日历
        Iterator<Calendar> iterator = DateUtils.iterator(datetime, DateUtils.RANGE_MONTH_SUNDAY);
        int i = 0;
        while (iterator.hasNext()) {
            i++;
            Calendar calendar = (Calendar) iterator.next();
            Date time = calendar.getTime();
            if (DateUtils.isSameDay(datetime, time)) {
                System.err.print(formatDateWeek(time) + "  ");
            } else {
                System.out.print(formatDateWeek(time) + "  ");
            }
            if (i % 7 == 0) {
                System.out.println();
            }
        }

        Calendar calendar = Calendar.getInstance();
        int j = calendar.get(Calendar.MONTH);
        System.out.println(j);
    }

}
